﻿using System;
using System.Collections.Generic;

namespace LOHDemo
{
	// This test, like TestInterlacedMemoryAllocationResultsInOutOfMemory
	// will allocate two lists of objects, but in a manner that is not 
	// interlaced because list A will be allocated first, then list B.
	// When the heap is then collected it will release a large block of
	// contiguous memory and we can continue to allocate large objects.
	public class TestNonInterlacedMemoryAllocationDoesNotResultInOutOfMemory : ITest
	{
		public void Execute(IHost host)
		{
			host.StartTest("Test two - filling LOH with non-interlaced small objects");
			host.MemoryUsed("Start of test");
			List<OneMegObject> first = new List<OneMegObject>();
			List<OneMegObject> second = new List<OneMegObject>();

			// Allocate memory until it runs out - this is a bit trickier as
			// we can't just allocate across the lists, instead lets do it in 100mb blocks.
			try
			{
				while (true)
				{
					for (int i = 0; i < 100; i++)
						first.Add(new OneMegObject());

					for (int i = 0; i < 100; i++)
						second.Add(new OneMegObject());
				}
			}
			catch (OutOfMemoryException)
			{
			}

			// LOH is now fill
			host.MemoryUsed("Memory is now full.");

			// Clear one of the lists which will result in the heap having
			// half memory free, but in what is now 100mb holes. Notice we force a full 
			// generation 2 collection.
			first.Clear();
			first = null;
			GC.Collect(2, GCCollectionMode.Forced);
			host.MemoryUsed("After freeing first list.");

			// Now we have plenty of free space on the heap, let's try to allocate
			// a larger 50mb chunk of memory.
			byte[] shouldfit = new byte[Constants.FiftyMeg];
			host.MemoryUsed("Successfully allocated a 50mb object");

			// Clean up after ourselves, release the second list and force collection
			shouldfit = null;
			second.Clear();
			second = null;
			GC.Collect(2, GCCollectionMode.Forced);

			host.MemoryUsed("All memory cleaned up");			
		}
	}
}
